/* Ledger.cpp
   Michael Zahniser, 2008

   Function definitions for the Ledger class.
*/

#include "Ledger.h"

#include <cmath>
#include <sstream>

using std::fabs;
using std::fixed;
using std::endl;
using std::ostringstream;



// Add a line to the ledger. The account name should be a series
// of token separated by " :: ". Each token will be made into a
// separate node of the tree containing a subtotal.
void Ledger::Add(string accountName, double balance)
{
    // Don't include accounts with zero balance.
    if(fabs(balance) < .001)
	return;
    
    const string SEPARATOR = " :: ";
    
    // Break the string up into tokens. Compare those tokens to the stack.
    vector<list<Line>::iterator>::iterator it = stack.begin();
    size_t pos = 0;

    // We are done matching existing items in the stack if the stack is empty.
    bool doneMatching = stack.empty();
    
    while(pos < accountName.length())
    {
	size_t endPos = accountName.find(SEPARATOR, pos);
	if(endPos == string::npos)
	    endPos = accountName.length();

	string token(accountName, pos, endPos - pos);

	// If this token doesn't match the stack, toss everything that is in
	// the stack from here on out.
	if(!doneMatching && (**it).name != token)
	{
	    stack.erase(it, stack.end());
	    doneMatching = true;
	}

	// Check to see if we're still matching the old stack, or creating
	// our own new nodes.
        if(doneMatching)
	{
	    // Add a line to the ledger for this new token, and store an
	    // iterator to it in order to update its subtotal later.
	    lines.push_back(
		Line(token, balance, static_cast<int>(stack.size())));
	    stack.push_back(--list<Line>::iterator(lines.end()));
	}
	else
	{
	    (**it).balance += balance;
	    ++it;
	    doneMatching = (it == stack.end());
	}

	pos = endPos + SEPARATOR.length();
    }
}



// Get the total of all accounts in the ledger.
double Ledger::Total() const
{
    double total = 0;
    
    for(list<Line>::const_iterator it = lines.begin(); it != lines.end(); ++it)
	if(!it->indent)
	    total += it->balance;

    return total;
}



// Print the ledger. If a total is specified, balances are shown both
// as actual amounts and as percentages of the total.
void Ledger::Print(ostream &out, double total) const
{
    for(list<Line>::const_iterator it = lines.begin(); it != lines.end(); ++it)
    {
	// Indent the line.
	out<<string(it->indent * 2, ' ');

	out.precision(2);
	out<<it->name<<": $"<<fixed<<it->balance;
	if(total)
	    out<<" ("<<(100.0 * it->balance / total + .005)<<"%)";
	out<<endl;
    }
}



// Print the ledger. If a total is specified, balances are shown both
// as actual amounts and as percentages of the total.
void Ledger::Print(TextTree &tree, double total) const
{
    for(list<Line>::const_iterator it = lines.begin(); it != lines.end(); ++it)
    {
	ostringstream out;
	out.precision(2);
	out<<it->name<<": $"<<fixed<<it->balance;
	if(total)
	    out<<" ("<<(100.0 * it->balance / total + .005)<<"%)";

	tree.Add(it->indent, out.str());
    }
}
